到昨天的學習下一章節就是挑戰做 todolist,我想挑戰不看老師的教學試著做出 todolist。這邊把主要的新增、刪除以及顯示已完成做項目出來就好,好那要開始囉!!!
在開始以前先把以下東西載入到 head
裡:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel='stylesheet' id='fontawesome-css' href='https://use.fontawesome.com/releases/v5.0.1/css/all.css?ver=4.9.1' type='text/css' media='all' />
<link href="https://fonts.googleapis.com/css?family=Varela+Round&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/reset.css">
<link rel="stylesheet" href="css/style.css">
<script src="https://unpkg.com/vue"></script>
<title>ToDoList</title>
</head>
畫面上最主要的東西有三個:1. Input textBox(輸入的部分) 2. List 表單(顯示及check) 3. 切換畫面。
<body>
<div id="app">
<div class="wrap">
<h1>Todos</h1>
<div class="list">
<ul>
<li>
<hr>
</li>
</ul>
</div>
<div class="inputBox">
<input type="text" class="textBox">
</div>
<div class="finishOrNot">
<span></span>
<span></span>
</div>
</div>
</div>
</body>
這邊比較需要說明的是,<li>
加入時是反向的加入,所以這邊就讓 <ul>
轉180度以及讓 <li>
轉-180度就能達到有下往上<li>
的加入。
* {
box-sizing: border-box;
}
html,
body {
width: 100%;
height: 100%;
}
body {
display: block;
color: #2E2E2E;
font-size: 18px;
font-family: 'Varela Round', sans-serif;
line-height: 1.5;
}
#app {
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
height: 100%;
height: 100%;
background: #bd83ce;
background: linear-gradient(45deg, #bd83ce, #ff9068);
}
.wrap {
display: flex;
align-items: center;
flex-direction: column;
justify-content: space-between;
padding: 3rem;
min-width: 380px;
min-height: 480px;
max-width: 50%;
max-height: 100%;
border-radius: 5px;
background: #fafafa;
box-shadow: 0 0 5px rgba(25, 25, 25, 0.25);
}
.wrap>h1 {
font-size: 2em;
font-family: 'Varela Round', sans-serif;
}
.list {
display: flex;
overflow: scroll;
flex-direction: column-reverse;
width: 200px;
height: 150px;
}
.list>ul {
transform: rotate(180deg);
}
.list>ul>li {
width: 100%;
transform: rotate(-180deg);
}
hr {
margin: 10px 0;
border: 1px solid rgb(202, 201, 201);
}
.inputBox {
margin-top: 15px;
width: 100%;
}
.textBox {
margin: 0;
padding: .5rem 1rem;
width: 100%;
outline: 0;
border: 2px solid #e8ebed;
border-radius: 5px;
background: #e8ebed;
transition: background .25s ease-out;
}
.doneOrTodo {
display: flex;
justify-content: space-between;
width: 100%;
}
.doneOrTodo span {
margin: 0 50px 0 50px;
}
.doneOrTodo>span:first-child::before {
display: inline-block;
color: #3498db;
content: "\f0ca";
font-weight: 900;
font-size: 25px;
font-family: 'Font Awesome 5 Free';
}
.doneOrTodo>span:last-child::before {
display: inline-block;
color: #2ecc71;
content: "\f00c";
font-weight: 900;
font-size: 25px;
font-family: 'Font Awesome 5 Free';
}
.complete {
text-decoration: line-through;
}
.visible {
display: none;
}
開始做 Vue application 時先想好我們會需要用到哪些的資料,以及其儲存的資料結構:
data: {
newTodo: "", // 用 v-model 來獲取 textBox 當下裡面的值
todos: [], // 把 textBox 裡面的值存到此陣列中,在藉由此陣列來把輸入的項目渲染到畫面上
done: [], // 儲存已完成的項目
notFinish: false, // 切換畫面用
finish: true, // 切換畫面用
}
建立完資料結構後就是要做 methods 的處理
第一步
我們先把 textBox 裡面的值動態得存入 newTodo
裡,使用 v-model
來達成。
第二步
用 @keyup.enter
這個 event handler 來觸發 addTodo
,這個 addTodo
是把 newTodo
這個變數加入到 todos
這個陣列裡,加入到陣列後把 newTodo
變為空,藉此來把 textBox 也變成空的(沒輸入任何東西的狀態)。
//HTML
<div class="inputBox">
<input type="text" class="textBox" @keyup.enter="addTodo" v-model="todo">
</div>
//VUE
addTodo(event) {
this.todos.push(this.newTodo);
this.newTodo = "";
}
//
第一步
先把 todos
陣列裡面的值全部打印到畫面上,這邊用 v-for 來逐一抓出資料並顯示。
第二步
當 check 的時候用 @click
event handler 來觸發 getData
function,這個 function 會把當前點擊的值給存到 done
這個陣列裡。
//HTML
<div class="list">
<ul @click="getData">
<li v-for="(item,index) in todos">
{{item}}
<hr>
</li>
</ul>
</div>
//VUE
getData(event) {
// 取得點擊的元素,也可以直接這樣用 event.target 就不用多宣告變數
const { target } = event;
// 判斷是不是點擊 <li> tag
if (target.tagName === "LI") {
// target.textContent 裡面會包含空格所以要用 trim() 來移除
this.done.push(target.textContent.trim());
// 刪除剛點擊的元素
this.todos.splice(this.todos.indexOf(target.textContent.trim()), 1);
};
}
我用比較簡單的方式來作切換畫面,這邊就再多做一個 ul
是顯示已完成的項目(done
陣列),在用 display:none
來做畫面的切換。
第一步
先用 v-bind
來綁定 visible
(display:none
) 這個 class ,在用兩個 flag(notFinish、finish),來做切換的依據。
第二步@click
event handler 來觸發 changeTodo
或 changeDone
,這兩個 function 是來判斷要不要讓 visible
class 加入到元素上。
<div class="list">
<ul @click="getData" :class="{'visible':notFinish}">
<li v-for="(item,index) in todos">
{{item}}
<hr>
</li>
</ul>
<ul :class="{'visible':finish}">
<li class="complete" v-for="(item,index) in done">
{{item}}
<hr>
</li>
</ul>
</div>
<div class="finishOrNot">
<span @click="changeTodo"></span>
<span @click="changeDone"></span>
</div>
//VUE
changeTodo(event) {
this.notFinish = false;
this.finish = true;
},
changeDone(event) {
this.notFinish = true;
this.finish = false;
}
<div id="app">
<div class="wrap">
<h1>Todos</h1>
<div class="list">
<ul @click="getData" :class="{'visible':notFinish}">
<li v-for="(item,index) in todos">
{{item}}
<hr>
</li>
</ul>
<ul :class="{'visible':finish}">
<li class="complete" v-for="(item,index) in done">
{{item}}
<hr>
</li>
</ul>
</div>
<div class="inputBox">
<input type="text" class="textBox" @keyup.enter="addTodo" v-model="newTodo">
</div>
<div class="finishOrNot">
<span @click="changeTodo"></span>
<span @click="changeDone"></span>
</div>
</div>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
newTodo: "",
todos: [],
done: [],
notFinish: false,
finish: true,
},
methods: {
addTodo(event) {
this.todos.push(this.newTodo);
this.newTodo = "";
},
getData(event) {
const {
target
} = event;
if (target.tagName === "LI") {
this.done.push(target.textContent.trim());
this.todos.splice(this.todos.indexOf(target.textContent.trim()), 1);
};
},
changeTodo(event) {
this.notFinish = false;
this.finish = true;
},
changeDone(event) {
this.notFinish = true;
this.finish = false;
}
}
});
</script>
我有想過,如果能把 v-for 裡面的陣列動態的切換的話,就不用兩個 ul
tag 了,不知道這樣行不行得通。
明天來看老師的影片,看看有沒有什麼好用的方法來製作。